﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace SymbolArrayControl
{
    public partial class SymbolArrayControl : UserControl
    {
        public List<String> Data = new List<String>();
        public Dictionary<String, String> Dic = new Dictionary<String, String>();

        public delegate void ChangedEventHandler(object sender, EventArgs e);
        public event ChangedEventHandler Changed;

        int _charSize = 64;
        int _columns = 12;
        int _index = 0;
        Font _font = new Font("Arial", 10);


        public int CharSize
        {
            get { return _charSize; }
            set
            {
                _charSize = value;
                UpdateScrollSize();
            }
        }

        public int Columns
        {
            get { return _columns; }
            set
            {
                _columns = value;
                UpdateScrollSize();
                ScrollToIndex();
            }
        }

        public int Rows
        {
            get { return (Data.Count + _columns - 1) / _columns; }
        }

        public void Clear()
        {
            Data.Clear();
            Dic.Clear();
            _index = 0;
            OnChanged(EventArgs.Empty);
        }

        public void Apply()
        {
            UpdateScrollSize();
            ScrollToIndex();
        }

        public SymbolArrayControl()
        {
            InitializeComponent();
        }

        protected virtual void OnChanged(EventArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }

        void UpdateScrollSize()
        {
            this.AutoScrollMinSize = new Size(this.Columns * _charSize, this.Rows * _charSize);
            this.Invalidate();
            this.Refresh();
        }

        void ScrollToIndex()
        {
            int x = this.HorizontalScroll.Value;
            int y = this.VerticalScroll.Value;
            Size size = this.ClientSize;
            int row = _index / _columns;
            int col = _index % _columns;
            int left = x / _charSize;
            int top = y / _charSize;
            int right = (x + size.Width) / _charSize;
            int bottom = (y + size.Height) / _charSize;

            if (col <= left || col >= right)
                x = col * _charSize;
            if (col >= right)
                x = x - size.Width + _charSize;

            if (row <= top || row >= bottom)
                y = row * _charSize;
            if (row >= bottom)
                y = y - size.Height + _charSize;

            this.SetDisplayRectLocation(-x, -y);
            this.AdjustFormScrollbars(true);
            this.Invalidate();
            this.Refresh();
        }

        private void SymbolArrayControl_Paint(object sender, PaintEventArgs e)
        {
            int x = this.HorizontalScroll.Value;
            int y = this.VerticalScroll.Value;
            Rectangle cr = e.ClipRectangle;
            Graphics g = e.Graphics;

            Pen pen = new Pen(Color.Black, 1);
            SolidBrush brush = new SolidBrush(Color.Black);
            SolidBrush brushBackground = new SolidBrush(Color.White);
            SolidBrush brushCursor = new SolidBrush(Color.Red);
            SolidBrush brushSame = new SolidBrush(Color.Gray);
            StringFormat format = new StringFormat(StringFormatFlags.NoClip);
            format.LineAlignment = StringAlignment.Center;
            format.Alignment = StringAlignment.Center;

            int toprow = (y + cr.Top) / _charSize;
            int bottomrow = (y + cr.Bottom + _charSize - 1) / _charSize;
            int leftcolumn = (x + cr.Left) / _charSize;

            for (int i = toprow * _columns; i < Data.Count && i <= bottomrow * _columns; i++)
            {
                String cipher = Data[i];
                String plain = Dic[Data[i]];
                int row = i / _columns;
                int col = i % _columns;
                Rectangle rect = new Rectangle(col * _charSize - x, row * _charSize - y, _charSize, _charSize);
                RectangleF rect_lefttop = new RectangleF(col * _charSize - x, row * _charSize - y, _charSize / 2, _charSize / 2);
                RectangleF rect_leftbottom = new RectangleF(col * _charSize - x, row * _charSize - y + _charSize / 2, _charSize / 2, _charSize / 2);
                RectangleF rect_righttop = new RectangleF(col * _charSize - x + _charSize / 2, row * _charSize - y, _charSize / 2, _charSize / 2);
                RectangleF rect_rightbottom = new RectangleF(col * _charSize - x + _charSize / 2, row * _charSize - y + _charSize / 2, _charSize / 2, _charSize / 2);
                g.FillRectangle((i == _index) ? brushCursor : (Data[i].CompareTo(Data[_index]) == 0) ? brushSame : brushBackground, rect);
                g.DrawRectangle(pen, rect);
                g.DrawString(cipher, _font, brush, rect_lefttop, format);
                g.DrawString(plain, _font, brush, rect_rightbottom, format);
            }
        }

        private void SymbolArrayControl_Scroll(object sender, ScrollEventArgs e)
        {
            this.Invalidate();
            this.Refresh();
        }


        private void SymbolArrayControl_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Left:
                    _index = Math.Max(0, _index - 1);
                    break;
                case Keys.Right:
                    _index = Math.Min(_index + 1, Data.Count - 1);
                    break;
                case Keys.Up:
                    _index = Math.Max(0, _index - _columns);
                    break;
                case Keys.Down:
                    _index = Math.Min(_index + _columns, Data.Count - 1);
                    break;
                case Keys.PageUp:
                case Keys.PageDown:
                    int rows = this.Height / _charSize;
                    switch (e.KeyCode)
                    {
                        case Keys.PageUp:
                            _index = Math.Max(0, _index - rows * _columns);
                            break;
                        case Keys.PageDown:
                            _index = Math.Min(_index + rows * _columns, Data.Count - 1);
                            break;
                    }
                    break;
                case Keys.Back:
                    _index = Math.Max(0, _index - 1);
                    Dic[Data[_index]] = "?";
                    OnChanged(EventArgs.Empty);
                    break;
            }
            ScrollToIndex();
        }

        private void SymbolArrayControl_MouseClick(object sender, MouseEventArgs e)
        {
            int x = this.HorizontalScroll.Value;
            int y = this.VerticalScroll.Value;
            int col = (x + e.X) / _charSize;
            int row = (y + e.Y) / _charSize;
            int i = row * _columns + col;
            if (col >= 0 && col < _columns && i >= 0 && i < Data.Count)
            {
                _index = i;
                ScrollToIndex();
            }
        }

        private void SymbolArrayControl_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            e.IsInputKey = true;
        }

        private void SymbolArrayControl_KeyPress(object sender, KeyPressEventArgs e)
        {
            switch (e.KeyChar)
            {
                case '\b':
                    break;
                default:
                    Dic[Data[_index]] = new String(e.KeyChar, 1);
                    _index = Math.Min(_index + 1, Data.Count);
                    OnChanged(EventArgs.Empty);
                    ScrollToIndex();
                    break;
            }
            e.Handled = true;
        }
    }
}
